Overview

Part 3: Histograms and Hypothesis Testing

Preparing the Data


library(tidyverse)
library(plotly)
library(reshape2)

Attaching package: ‘reshape2’

The following object is masked from ‘package:tidyr’:

    smiths
exif <- read_csv("capstone_exif.csv")
Rows: 1116 Columns: 15── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (10): DateTimeOriginal, CreateDate, ModifyDate, Software, LensInfo, LensModel, ExposureTime, FocalLength, FocalLengthIn3...
dbl  (5): FlickrID, JFIFVersion, ISO, FNumber, BrightnessValue
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
img_data <- read_csv("capstone_img_data.csv")
Rows: 10955 Columns: 87── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (16): flickr, img_loc, the_image, crop_coords, center_rgb, post_top_hsl, post_2_hsl, post_3_hsl, post_4_hsl, post_5_hsl,...
dbl (70): using_id, img_width, img_height, do_img_at, sub_img, full_id, r_min, r_max, r_mean, r_mode, g_min, g_max, g_mean, ...
num  (1): vivid_count
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

# after working with this data for 12 hours, I find out there are hidden characeters in my data that were not displaying in Excel.

imgsd_tidy$post_top_count <- as.integer(imgsd_tidy$post_top_count)
imgsd_tidy$post_2_count <- as.integer(imgsd_tidy$post_2_count)
imgsd_tidy$post_3_count <- as.integer(imgsd_tidy$post_3_count)
imgsd_tidy$post_4_count <- as.integer(imgsd_tidy$post_4_count)
imgsd_tidy$post_5_count <- as.integer(imgsd_tidy$post_5_count)
imgsd_tidy$post_6_count <- as.integer(imgsd_tidy$post_6_count)

imgsd_tidy$common_hsl_1_count <- as.integer(imgsd_tidy$common_hsl_1_count)
imgsd_tidy$common_hsl_2_count <- as.integer(imgsd_tidy$common_hsl_2_count)
imgsd_tidy$common_hsl_3_count <- as.integer(imgsd_tidy$common_hsl_3_count)
imgsd_tidy$common_hsl_4_count <- as.integer(imgsd_tidy$common_hsl_4_count)

imgsd_tidy$full_red_count <- as.integer(imgsd_tidy$full_red_count)
imgsd_tidy$full_orange_count <- as.integer(imgsd_tidy$full_orange_count)
imgsd_tidy$full_yellow_count <- as.integer(imgsd_tidy$full_yellow_count)
imgsd_tidy$full_green_count <- as.integer(imgsd_tidy$full_green_count)
imgsd_tidy$full_cyan_count <- as.integer(imgsd_tidy$full_cyan_count)
imgsd_tidy$full_blue_count <- as.integer(imgsd_tidy$full_blue_count)
imgsd_tidy$full_purple_count <- as.integer(imgsd_tidy$full_purple_count)
imgsd_tidy$full_mag_count <- as.integer(imgsd_tidy$full_mag_count)
imgsd_tidy$visib_red_count <- as.integer(imgsd_tidy$visib_red_count)
imgsd_tidy$visib_orange_count <- as.integer(imgsd_tidy$visib_orange_count)
imgsd_tidy$visib_yellow_count <- as.integer(imgsd_tidy$visib_yellow_count)
imgsd_tidy$visib_green_count <- as.integer(imgsd_tidy$visib_green_count)
imgsd_tidy$visib_cyan_count <- as.integer(imgsd_tidy$visib_cyan_count)
imgsd_tidy$visib_blue_count <- as.integer(imgsd_tidy$visib_blue_count)
imgsd_tidy$visib_purple_count <- as.integer(imgsd_tidy$visib_purple_count)
imgsd_tidy$visib_mag_count <- as.integer(imgsd_tidy$visib_mag_count)
imgsd_tidy$vivid_red_count <- as.integer(imgsd_tidy$vivid_red_count)
imgsd_tidy$vivid_orange_count <- as.integer(imgsd_tidy$vivid_orange_count)
imgsd_tidy$vivid_yellow_count <- as.integer(imgsd_tidy$vivid_yellow_count)
imgsd_tidy$vivid_green_count <- as.integer(imgsd_tidy$vivid_green_count)
imgsd_tidy$vivid_cyan_count <- as.integer(imgsd_tidy$vivid_cyan_count)
imgsd_tidy$vivid_blue_count <- as.integer(imgsd_tidy$vivid_blue_count)
imgsd_tidy$vivid_purple_count <- as.integer(imgsd_tidy$vivid_purple_count)
imgsd_tidy$vivid_mag_count <- as.integer(imgsd_tidy$vivid_mag_count)
imgsd_tidy$vivid_count <- as.integer(imgsd_tidy$vivid_count)

imgsd_tidy$gen_bright_count <- as.integer(imgsd_tidy$gen_bright_count)
imgsd_tidy$gen_dark_count <- as.integer(imgsd_tidy$gen_dark_count)

imgsd_tidy <- imgsd_tidy %>% mutate(total_pixels = full_red_count +
                                      full_orange_count +
                                      full_yellow_count +
                                      full_green_count +
                                      full_cyan_count +
                                      full_blue_count + 
                                      full_purple_count +
                                      full_mag_count)

# more type conversions
imgsd_tidy$total_pixels <- as.integer(imgsd_tidy$total_pixels)
imgsd_tidy$light_mean_val <- as.numeric(imgsd_tidy$light_mean_val)
imgsd_tidy$post_num_regions <- as.integer(imgsd_tidy$post_num_regions)
imgsd_tidy$r_mean <- as.numeric(imgsd_tidy$r_mean)
imgsd_tidy$g_mean <- as.numeric(imgsd_tidy$g_mean)
imgsd_tidy$b_mean <- as.numeric(imgsd_tidy$b_mean)


#establishing ratio columns
imgsd_ratio <- imgsd_tidy %>% 
  mutate(ratio_post_top_hsl = post_top_count / total_pixels) %>%
  mutate(ratio_post_2_hsl = post_2_count / total_pixels) %>%
  mutate(ratio_post_3_hsl = post_3_count / total_pixels) %>%
  mutate(ratio_post_4_hsl = post_4_count / total_pixels) %>%
  mutate(ratio_post_5_hsl = post_5_count / total_pixels) %>%
  mutate(ratio_post_6_hsl = post_6_count / total_pixels) %>%
  mutate(ratio_full_red = full_red_count / total_pixels) %>%
  mutate(ratio_full_oragne = full_orange_count / total_pixels) %>%
  mutate(ratio_full_yellow = full_yellow_count / total_pixels) %>%
  mutate(ratio_full_green = full_green_count / total_pixels) %>%  
  mutate(ratio_full_cyan = full_cyan_count / total_pixels) %>%  
  mutate(ratio_full_blue = full_blue_count / total_pixels) %>%  
  mutate(ratio_full_purple = full_purple_count / total_pixels) %>%  
  mutate(ratio_full_mag = full_mag_count / total_pixels) %>%  
  mutate(ratio_full_red = full_red_count / total_pixels) %>%
  mutate(ratio_visib_red = visib_red_count / total_pixels) %>%
  mutate(ratio_visib_oragne = visib_orange_count / total_pixels) %>%
  mutate(ratio_visib_yellow = visib_yellow_count / total_pixels) %>%
  mutate(ratio_visib_green = visib_green_count / total_pixels) %>%  
  mutate(ratio_visib_cyan = visib_cyan_count / total_pixels) %>%  
  mutate(ratio_visib_blue = visib_blue_count / total_pixels) %>%  
  mutate(ratio_visib_purple = visib_purple_count / total_pixels) %>%  
  mutate(ratio_visib_mag = visib_mag_count / total_pixels) %>%
  mutate(ratio_vivid_red = vivid_red_count / total_pixels) %>%
  mutate(ratio_vivid_oragne = vivid_orange_count / total_pixels) %>%
  mutate(ratio_vivid_yellow = vivid_yellow_count / total_pixels) %>%
  mutate(ratio_vivid_green = vivid_green_count / total_pixels) %>%  
  mutate(ratio_vivid_cyan = vivid_cyan_count / total_pixels) %>%  
  mutate(ratio_vivid_blue = vivid_blue_count / total_pixels) %>%  
  mutate(ratio_vivid_purple = vivid_purple_count / total_pixels) %>%  
  mutate(ratio_vivid_mag = vivid_mag_count / total_pixels) %>%
  mutate(ratio_vivid = vivid_count / total_pixels)
imgsd_tidy <- select(img_data, -c(flickr, img_loc, the_image, img_width, img_height, crop_coords, do_img_at, r_mode, b_mode, g_mode))

imgsd_tidy <- replace_na(imgsd_tidy, list(
  post_2_hsl = "(-1, -1, -1)",
  post_3_hsl = "(-1, -1, -1)",
  post_4_hsl = "(-1, -1, -1)",
  post_5_hsl = "(-1, -1, -1)",
  post_6_hsl = "(-1, -1, -1)"
  )
  )

exif_tidy <- exif_tidy %>% separate(create_date, into = c('full_date', 'time'), sep = " ", remove = TRUE) %>% separate(full_date, into = c('year', 'month', 'day'), sep = ":", remove = FALSE) 

exif_tidy$date <- as.Date(paste("1881", exif_tidy$month, exif_tidy$day, sep = "-"), format ="%Y-%m-%d")

subimg_qty <- imgsd_tidy %>% count(using_id)

good_ids <- subimg_qty[subimg_qty$n >=6, "using_id"]

imgsd_tidy <- imgsd_tidy %>% filter(using_id %in% good_ids$using_id)

exif_tidy <- exif_tidy %>% filter(flickr_id %in% good_ids$using_id)

# after working with this data for 12 hours, I find out there are hidden characeters in my data that were not displaying in Excel.

imgsd_tidy$post_top_count <- as.integer(imgsd_tidy$post_top_count)
imgsd_tidy$post_2_count <- as.integer(imgsd_tidy$post_2_count)
imgsd_tidy$post_3_count <- as.integer(imgsd_tidy$post_3_count)
imgsd_tidy$post_4_count <- as.integer(imgsd_tidy$post_4_count)
imgsd_tidy$post_5_count <- as.integer(imgsd_tidy$post_5_count)
imgsd_tidy$post_6_count <- as.integer(imgsd_tidy$post_6_count)

imgsd_tidy$common_hsl_1_count <- as.integer(imgsd_tidy$common_hsl_1_count)
imgsd_tidy$common_hsl_2_count <- as.integer(imgsd_tidy$common_hsl_2_count)
imgsd_tidy$common_hsl_3_count <- as.integer(imgsd_tidy$common_hsl_3_count)
imgsd_tidy$common_hsl_4_count <- as.integer(imgsd_tidy$common_hsl_4_count)

imgsd_tidy$full_red_count <- as.integer(imgsd_tidy$full_red_count)
imgsd_tidy$full_orange_count <- as.integer(imgsd_tidy$full_orange_count)
imgsd_tidy$full_yellow_count <- as.integer(imgsd_tidy$full_yellow_count)
imgsd_tidy$full_green_count <- as.integer(imgsd_tidy$full_green_count)
imgsd_tidy$full_cyan_count <- as.integer(imgsd_tidy$full_cyan_count)
imgsd_tidy$full_blue_count <- as.integer(imgsd_tidy$full_blue_count)
imgsd_tidy$full_purple_count <- as.integer(imgsd_tidy$full_purple_count)
imgsd_tidy$full_mag_count <- as.integer(imgsd_tidy$full_mag_count)
imgsd_tidy$visib_red_count <- as.integer(imgsd_tidy$visib_red_count)
imgsd_tidy$visib_orange_count <- as.integer(imgsd_tidy$visib_orange_count)
imgsd_tidy$visib_yellow_count <- as.integer(imgsd_tidy$visib_yellow_count)
imgsd_tidy$visib_green_count <- as.integer(imgsd_tidy$visib_green_count)
imgsd_tidy$visib_cyan_count <- as.integer(imgsd_tidy$visib_cyan_count)
imgsd_tidy$visib_blue_count <- as.integer(imgsd_tidy$visib_blue_count)
imgsd_tidy$visib_purple_count <- as.integer(imgsd_tidy$visib_purple_count)
imgsd_tidy$visib_mag_count <- as.integer(imgsd_tidy$visib_mag_count)
imgsd_tidy$vivid_red_count <- as.integer(imgsd_tidy$vivid_red_count)
imgsd_tidy$vivid_orange_count <- as.integer(imgsd_tidy$vivid_orange_count)
imgsd_tidy$vivid_yellow_count <- as.integer(imgsd_tidy$vivid_yellow_count)
imgsd_tidy$vivid_green_count <- as.integer(imgsd_tidy$vivid_green_count)
imgsd_tidy$vivid_cyan_count <- as.integer(imgsd_tidy$vivid_cyan_count)
imgsd_tidy$vivid_blue_count <- as.integer(imgsd_tidy$vivid_blue_count)
imgsd_tidy$vivid_purple_count <- as.integer(imgsd_tidy$vivid_purple_count)
imgsd_tidy$vivid_mag_count <- as.integer(imgsd_tidy$vivid_mag_count)
imgsd_tidy$vivid_count <- as.integer(imgsd_tidy$vivid_count)

imgsd_tidy$gen_bright_count <- as.integer(imgsd_tidy$gen_bright_count)
imgsd_tidy$gen_dark_count <- as.integer(imgsd_tidy$gen_dark_count)

imgsd_tidy <- imgsd_tidy %>% mutate(total_pixels = full_red_count +
                                      full_orange_count +
                                      full_yellow_count +
                                      full_green_count +
                                      full_cyan_count +
                                      full_blue_count + 
                                      full_purple_count +
                                      full_mag_count)

# more type conversions
imgsd_tidy$total_pixels <- as.integer(imgsd_tidy$total_pixels)
imgsd_tidy$light_mean_val <- as.numeric(imgsd_tidy$light_mean_val)
imgsd_tidy$post_num_regions <- as.integer(imgsd_tidy$post_num_regions)
imgsd_tidy$r_mean <- as.numeric(imgsd_tidy$r_mean)
imgsd_tidy$g_mean <- as.numeric(imgsd_tidy$g_mean)
imgsd_tidy$b_mean <- as.numeric(imgsd_tidy$b_mean)


#establishing ratio columns
imgsd_ratio <- imgsd_tidy %>% 
  mutate(ratio_post_top_hsl = post_top_count / total_pixels) %>%
  mutate(ratio_post_2_hsl = post_2_count / total_pixels) %>%
  mutate(ratio_post_3_hsl = post_3_count / total_pixels) %>%
  mutate(ratio_post_4_hsl = post_4_count / total_pixels) %>%
  mutate(ratio_post_5_hsl = post_5_count / total_pixels) %>%
  mutate(ratio_post_6_hsl = post_6_count / total_pixels) %>%
  mutate(ratio_full_red = full_red_count / total_pixels) %>%
  mutate(ratio_full_oragne = full_orange_count / total_pixels) %>%
  mutate(ratio_full_yellow = full_yellow_count / total_pixels) %>%
  mutate(ratio_full_green = full_green_count / total_pixels) %>%  
  mutate(ratio_full_cyan = full_cyan_count / total_pixels) %>%  
  mutate(ratio_full_blue = full_blue_count / total_pixels) %>%  
  mutate(ratio_full_purple = full_purple_count / total_pixels) %>%  
  mutate(ratio_full_mag = full_mag_count / total_pixels) %>%  
  mutate(ratio_full_red = full_red_count / total_pixels) %>%
  mutate(ratio_visib_red = visib_red_count / total_pixels) %>%
  mutate(ratio_visib_oragne = visib_orange_count / total_pixels) %>%
  mutate(ratio_visib_yellow = visib_yellow_count / total_pixels) %>%
  mutate(ratio_visib_green = visib_green_count / total_pixels) %>%  
  mutate(ratio_visib_cyan = visib_cyan_count / total_pixels) %>%  
  mutate(ratio_visib_blue = visib_blue_count / total_pixels) %>%  
  mutate(ratio_visib_purple = visib_purple_count / total_pixels) %>%  
  mutate(ratio_visib_mag = visib_mag_count / total_pixels) %>%
  mutate(ratio_vivid_red = vivid_red_count / total_pixels) %>%
  mutate(ratio_vivid_oragne = vivid_orange_count / total_pixels) %>%
  mutate(ratio_vivid_yellow = vivid_yellow_count / total_pixels) %>%
  mutate(ratio_vivid_green = vivid_green_count / total_pixels) %>%  
  mutate(ratio_vivid_cyan = vivid_cyan_count / total_pixels) %>%  
  mutate(ratio_vivid_blue = vivid_blue_count / total_pixels) %>%  
  mutate(ratio_vivid_purple = vivid_purple_count / total_pixels) %>%  
  mutate(ratio_vivid_mag = vivid_mag_count / total_pixels) %>%
  mutate(ratio_vivid = vivid_count / total_pixels)

Exploration

1D Histograms

  • Vivid %

Due to a very large number of images that have fewer than 2% vivid pixels, Vivid Ratios are separated out into three segments: 0% vivid pixels, 0-1% vivid pixels, and more than 1% vivid pixels


vivid_ratio <- imgsd_ratio %>% select(ratio_vivid)
vivid_ratio$ratio_vivid <- as.numeric(vivid_ratio$ratio_vivid)

vivid_ratio <- vivid_ratio %>% mutate(
  ratio_rank = case_when(
    ratio_vivid == 0 ~ 0, 
    ratio_vivid > 0 & ratio_vivid < 0.01 ~ 1, 
    ratio_vivid >= 0.01 ~ 2)
  )

rr_counts <- count(vivid_ratio, ratio_rank)

vrr_pie <- plot_ly(rr_counts, labels=~factor(ratio_rank), values= ~n, type='pie')

vrr_pie
# 0 -> No Vivid Pixels
# 1 -> 0-1% vivid pixels
# 2 -> more than 1% vivid pixels

Less vivid pixels than expected! Way less! Only 359 images/subimages had more than 1% of pixels with more than 70% saturation and lightness between 40 and 70%

Now let’s get back to seeing the distribution of those 359 values:


vivid_hist <- vivid_ratio %>% filter(ratio_rank == 2)

fig <- plot_ly(vivid_hist,
               x= ~ratio_vivid,
               type = "histogram")

fig

Future investigation will perform these calculations on the main dataframe in order to access specific image IDs and evaluate the subjective qualities of the top vivid images.

  • Post-num (filter by img segment)

Investigating the distribution of how many posterization “clumps” were in each image


#first setting up filters for more useful faceting going forward
r_fig_filter_0 <- imgsd_ratio %>% filter(imgsd_ratio$sub_img == 0)
r_fig_filter_a <- imgsd_ratio[imgsd_ratio$sub_img %in% c(1, 2, 3), ]
r_fig_filter_b <- imgsd_ratio[imgsd_ratio$sub_img %in% c(4, 5, 6), ]
r_fig_filter_c <- imgsd_ratio[imgsd_ratio$sub_img %in% c(7, 8, 9), ]


fig_0 <- ggplot(r_fig_filter_0,
               aes(x=post_num_regions)) + 
                geom_histogram(binwidth = 5)

fig_a <- ggplot(r_fig_filter_a,
              aes(x=post_num_regions)) + 
              geom_histogram(binwidth = 5) +
              facet_grid (~ sub_img)

fig_b <- ggplot(r_fig_filter_b,
              aes(x=post_num_regions)) + 
              geom_histogram(binwidth = 5) +
              facet_grid (~ sub_img)

fig_c <- ggplot(r_fig_filter_c,
              aes(x=post_num_regions)) + 
              geom_histogram(binwidth = 5) +
              facet_grid (~ sub_img)

fig_0b <- ggplot(r_fig_filter_0,
               aes(x=post_num_regions)) + 
                geom_histogram(binwidth = 50)

fig_0

fig_0b

fig_a

fig_b

fig_c

No huge conclusions to draw about the distribution in sub_image 0, other than the x-scale is shown as roughly 10x (slightly less) than the subimages. Also mildly interesting (also on sub_img 0), the max count for any given bin (at bin-width 5), is a fraction of the scale of the other sub_images. Bumping the sub_img 0 binwidth up to 50 (10x), brings it more in line with the counts in the sub_images, but the long tail on sub_img 0 keeps it from matching exactly.

Otherwise, the only noteworthy observation is the less-intense right skew on sub_images 2, 5, and 8.

These sub-images represent the horizontal middle third of their source images, mimicking the artistic principles of the rule of thirds and putting focal points near the center of the image.

  • Exif brightness

EXIF data, I have not forsaken you.




fig <- ggplot(exif_tidy,
               aes(x=brightness_value)) + 
                geom_histogram(bins=250)

fig

That’s the most normal distribution we’ve seen yet! But still pretty irregular. Let’s see how it compares to the HSL data

  • Mean_lightness

fig_all <- ggplot(imgsd_ratio,
               aes(x=light_mean_val)) + 
                geom_histogram(bins=250)

fig_0 <- ggplot(r_fig_filter_0,
               aes(x=light_mean_val)) + 
                geom_histogram(bins=250)

fig_a <- ggplot(r_fig_filter_a,
              aes(x=light_mean_val)) + 
              geom_histogram(bins=250) +
              facet_grid(~ sub_img)

fig_b <- ggplot(r_fig_filter_b,
              aes(x=light_mean_val)) + 
              geom_histogram(bins=250) +
              facet_grid(~ sub_img)

fig_c <- ggplot(r_fig_filter_c,
              aes(x=light_mean_val)) + 
              geom_histogram(bins=250) +
              facet_grid(~ sub_img)


fig_all

fig_0

fig_a

fig_b

fig_c

Observations: Variation among sub-images is fairly minor, with some some possibility that corner quadrants (1, 3, 7, 9) have wider distributions overall. Lightness values processed for this dataset are centered around 0.625 with a slight left skew. While the EXIF data has a less-normal distribution, it does seem to have a center slightly to the right of its baseline value, but the irregularity of the shape looks more like a right skew than left.

  • (r/g/b) mean

fig <- plot_ly(imgsd_ratio, alpha = .4)
fig <- fig %>% add_histogram(x = ~r_mean)
fig <- fig %>% add_histogram(x = ~g_mean)
fig <- fig %>% add_histogram(x = ~b_mean)
fig <- fig %>% layout(barmode = 'overlay', colorway=c('red', 'green', 'blue'))

fig

Observation: No special information here. As expected, it looks a lot like the mean lightness value distrbution. Almost as if r+g+b = light….

  • Date distribution (with and without year)

fig <- plot_ly(exif_tidy, x = ~date, type = 'histogram')

fig_all <- plot_ly(exif_tidy, x = ~full_date, type = 'histogram')

fig
fig_all

Observation: Even when adjusting for year, the distribution of images is highly irregular. As a month, August is over-represented, and as an individual date, Will and Taylor’s wedding is over-represented.

exif_tidy$time <- strptime(exif_tidy$time, format = "%H:%M%S")

fig <- plot_ly(exif_tidy, x = ~time, type = 'histogram')
fig
Error: C stack usage  15924272 is too close to the limit

Analysis

Final data prep!

Slimming down the EXIF data frame….


#slimming down exif columns....

simple_exif <- exif_tidy %>% select(c(flickr_id, date, month, time, software, jfifversion, brightness_value))

… and adding those EXIF columns to the main dataframe


simple_exif <- simple_exif %>%
  mutate(flickr_id = as.character(flickr_id))

# bringing it all full circle with this variable choice

all_img_data <- imgsd_ratio %>% 
  left_join(simple_exif, by = c("using_id" = "flickr_id"))
Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
#this throws a warning about many-to-many relationships, but this is expected behavior

This space reserved for re-creating sub-frames for ‘faceting’

At last, let’s revisit those hypotheses

H1 - Hue vs Date

\(Ho:\) There is no correlation between time of year and color values

\(Ha:\) Warm color values are more prominent between May and September

Gut Check - Due to the uneven distribution of sample images over time, I don’t have a strong expectation of valid results.


exif_tidy$month <- as.integer(exif_tidy$month)

season_split <- exif_tidy %>% mutate(
  season = case_when(
    month >= 5 & month <= 9 ~ 1, TRUE ~ 0)
  )

season_counts <- table(season_split$season)
season_data <- data.frame(ratio = names(season_counts), count = as.vector(season_counts))

season_data

On the other hand, summer values (622) don’t outrageously outnumber non-summer values.

Now to collect the hue information we will be comparing


all_img_data <- all_img_data %>% 
  mutate(visib_warm = visib_red_count + visib_orange_count + visib_yellow_count + visib_mag_count) %>%
  mutate(visib_cool = visib_green_count + visib_cyan_count + visib_blue_count + visib_purple_count)
  
h1_frame <- all_img_data %>% 
  select(c(using_id, sub_img, total_pixels, visib_warm, visib_cool, date, month)) %>% 
  mutate(warm_ratio = visib_warm / total_pixels) %>% 
  mutate(cool_ratio = visib_cool/total_pixels) %>% 
  mutate(season = case_when(month >= 5 & month <= 9 ~ 1, TRUE ~ 0)) %>%
  mutate(ratio_diff = warm_ratio - cool_ratio)

Testing Time

t.test(warm_ratio ~ season, h1_frame)

    Welch Two Sample t-test

data:  warm_ratio by season
t = 11.179, df = 9078.6, p-value < 2.2e-16
alternative hypothesis: true difference in means between group 0 and group 1 is not equal to 0
95 percent confidence interval:
 0.01619583 0.02308347
sample estimates:
mean in group 0 mean in group 1 
     0.04115762      0.02151798 

Conclusion

Despite my hesitation, the results of the T-test are strongly in favor of rejecting the null hypothesis that there is no difference in visible warmth throughout the year. A high t-value and low p-value are both in support of this conclusion.

Post Script:

Additional Hypotheses:

H2 - Lightness vs Time

\(Ho:\) There is no correlation between time of day and lightness values

\(Ha:\) Lightness values are higher between 6 am and 6pm

H3 - Saturation vs Subject

\(Ho:\) There is no correlation between saturation and being a picture of my cat

\(Ha:\) Low saturation values are increasingly common over time, especially in central sub-images

H4 - Vividness vs Image Type

\(Ho:\) Vivid ratio (percentage of vivid pixels) is uniformly distributed among all Software types

\(Ha:\) Vivid ratio is consistently highest in Slow Shutter Cam photos without JFIF values

LS0tDQp0aXRsZTogIkRhdGFzZXQgQ2Fwc3RvbmUiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIE92ZXJ2aWV3DQoNCiMgUGFydCAzOiBIaXN0b2dyYW1zIGFuZCBIeXBvdGhlc2lzIFRlc3RpbmcNCg0KIyMgUHJlcGFyaW5nIHRoZSBEYXRhDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShyZXNoYXBlMikNCg0KYGBgDQoNCmBgYHtyfQ0KZXhpZiA8LSByZWFkX2NzdigiY2Fwc3RvbmVfZXhpZi5jc3YiKQ0KaW1nX2RhdGEgPC0gcmVhZF9jc3YoImNhcHN0b25lX2ltZ19kYXRhLmNzdiIpDQoNCmBgYA0KDQpgYGB7cn0NCm5hbWVzKGV4aWYpIDwtIGdzdWIoIihbYS16MC05XSkoW0EtWl0pIiwgIlxcMV9cXDIiLCBuYW1lcyhleGlmKSkNCm5hbWVzKGV4aWYpIDwtIG5hbWVzKGV4aWYpICU+JSB0b2xvd2VyKCkNCg0KZXhpZl90aWR5IDwtIHNlbGVjdChleGlmLCAtYyhkYXRlX3RpbWVfb3JpZ2luYWwsIG1vZGlmeV9kYXRlLCBsZW5zX2luZm8sIGZudW1iZXIsIGZvY2FsX2xlbmd0aCkpDQpleGlmX3RpZHkgPC0gcmVwbGFjZV9uYShleGlmX3RpZHksIGxpc3Qoc3ViamVjdF9hcmVhID0gIjAgMCAwIDAiLCBqZmlmdmVyc2lvbiA9IDApKQ0KDQpgYGANCg0KYGBge3J9DQppbWdzZF90aWR5IDwtIHNlbGVjdChpbWdfZGF0YSwgLWMoZmxpY2tyLCBpbWdfbG9jLCB0aGVfaW1hZ2UsIGltZ193aWR0aCwgaW1nX2hlaWdodCwgY3JvcF9jb29yZHMsIGRvX2ltZ19hdCwgcl9tb2RlLCBiX21vZGUsIGdfbW9kZSkpDQoNCmltZ3NkX3RpZHkgPC0gcmVwbGFjZV9uYShpbWdzZF90aWR5LCBsaXN0KA0KICBwb3N0XzJfaHNsID0gIigtMSwgLTEsIC0xKSIsDQogIHBvc3RfM19oc2wgPSAiKC0xLCAtMSwgLTEpIiwNCiAgcG9zdF80X2hzbCA9ICIoLTEsIC0xLCAtMSkiLA0KICBwb3N0XzVfaHNsID0gIigtMSwgLTEsIC0xKSIsDQogIHBvc3RfNl9oc2wgPSAiKC0xLCAtMSwgLTEpIg0KICApDQogICkNCg0KYGBgDQoNCmBgYHtyfQ0KDQpleGlmX3RpZHkgPC0gZXhpZl90aWR5ICU+JSBzZXBhcmF0ZShjcmVhdGVfZGF0ZSwgaW50byA9IGMoJ2Z1bGxfZGF0ZScsICd0aW1lJyksIHNlcCA9ICIgIiwgcmVtb3ZlID0gVFJVRSkgJT4lIHNlcGFyYXRlKGZ1bGxfZGF0ZSwgaW50byA9IGMoJ3llYXInLCAnbW9udGgnLCAnZGF5JyksIHNlcCA9ICI6IiwgcmVtb3ZlID0gRkFMU0UpIA0KDQpleGlmX3RpZHkkZGF0ZSA8LSBhcy5EYXRlKHBhc3RlKCIxODgxIiwgZXhpZl90aWR5JG1vbnRoLCBleGlmX3RpZHkkZGF5LCBzZXAgPSAiLSIpLCBmb3JtYXQgPSIlWS0lbS0lZCIpDQpgYGANCg0KYGBge3J9DQoNCnN1YmltZ19xdHkgPC0gaW1nc2RfdGlkeSAlPiUgY291bnQodXNpbmdfaWQpDQoNCmBgYA0KDQpgYGB7cn0NCg0KZ29vZF9pZHMgPC0gc3ViaW1nX3F0eVtzdWJpbWdfcXR5JG4gPj02LCAidXNpbmdfaWQiXQ0KDQppbWdzZF90aWR5IDwtIGltZ3NkX3RpZHkgJT4lIGZpbHRlcih1c2luZ19pZCAlaW4lIGdvb2RfaWRzJHVzaW5nX2lkKQ0KDQpleGlmX3RpZHkgPC0gZXhpZl90aWR5ICU+JSBmaWx0ZXIoZmxpY2tyX2lkICVpbiUgZ29vZF9pZHMkdXNpbmdfaWQpDQoNCmBgYA0KDQpgYGB7cn0NCg0KIyBhZnRlciB3b3JraW5nIHdpdGggdGhpcyBkYXRhIGZvciAxMiBob3VycywgSSBmaW5kIG91dCB0aGVyZSBhcmUgaGlkZGVuIGNoYXJhY2V0ZXJzIGluIG15IGRhdGEgdGhhdCB3ZXJlIG5vdCBkaXNwbGF5aW5nIGluIEV4Y2VsLg0KDQppbWdzZF90aWR5JHBvc3RfdG9wX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSRwb3N0X3RvcF9jb3VudCkNCmltZ3NkX3RpZHkkcG9zdF8yX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSRwb3N0XzJfY291bnQpDQppbWdzZF90aWR5JHBvc3RfM19jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkcG9zdF8zX2NvdW50KQ0KaW1nc2RfdGlkeSRwb3N0XzRfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHBvc3RfNF9jb3VudCkNCmltZ3NkX3RpZHkkcG9zdF81X2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSRwb3N0XzVfY291bnQpDQppbWdzZF90aWR5JHBvc3RfNl9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkcG9zdF82X2NvdW50KQ0KDQppbWdzZF90aWR5JGNvbW1vbl9oc2xfMV9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkY29tbW9uX2hzbF8xX2NvdW50KQ0KaW1nc2RfdGlkeSRjb21tb25faHNsXzJfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JGNvbW1vbl9oc2xfMl9jb3VudCkNCmltZ3NkX3RpZHkkY29tbW9uX2hzbF8zX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSRjb21tb25faHNsXzNfY291bnQpDQppbWdzZF90aWR5JGNvbW1vbl9oc2xfNF9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkY29tbW9uX2hzbF80X2NvdW50KQ0KDQppbWdzZF90aWR5JGZ1bGxfcmVkX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSRmdWxsX3JlZF9jb3VudCkNCmltZ3NkX3RpZHkkZnVsbF9vcmFuZ2VfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JGZ1bGxfb3JhbmdlX2NvdW50KQ0KaW1nc2RfdGlkeSRmdWxsX3llbGxvd19jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkZnVsbF95ZWxsb3dfY291bnQpDQppbWdzZF90aWR5JGZ1bGxfZ3JlZW5fY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JGZ1bGxfZ3JlZW5fY291bnQpDQppbWdzZF90aWR5JGZ1bGxfY3lhbl9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkZnVsbF9jeWFuX2NvdW50KQ0KaW1nc2RfdGlkeSRmdWxsX2JsdWVfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JGZ1bGxfYmx1ZV9jb3VudCkNCmltZ3NkX3RpZHkkZnVsbF9wdXJwbGVfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JGZ1bGxfcHVycGxlX2NvdW50KQ0KaW1nc2RfdGlkeSRmdWxsX21hZ19jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkZnVsbF9tYWdfY291bnQpDQppbWdzZF90aWR5JHZpc2liX3JlZF9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkdmlzaWJfcmVkX2NvdW50KQ0KaW1nc2RfdGlkeSR2aXNpYl9vcmFuZ2VfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHZpc2liX29yYW5nZV9jb3VudCkNCmltZ3NkX3RpZHkkdmlzaWJfeWVsbG93X2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSR2aXNpYl95ZWxsb3dfY291bnQpDQppbWdzZF90aWR5JHZpc2liX2dyZWVuX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSR2aXNpYl9ncmVlbl9jb3VudCkNCmltZ3NkX3RpZHkkdmlzaWJfY3lhbl9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkdmlzaWJfY3lhbl9jb3VudCkNCmltZ3NkX3RpZHkkdmlzaWJfYmx1ZV9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkdmlzaWJfYmx1ZV9jb3VudCkNCmltZ3NkX3RpZHkkdmlzaWJfcHVycGxlX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSR2aXNpYl9wdXJwbGVfY291bnQpDQppbWdzZF90aWR5JHZpc2liX21hZ19jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkdmlzaWJfbWFnX2NvdW50KQ0KaW1nc2RfdGlkeSR2aXZpZF9yZWRfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHZpdmlkX3JlZF9jb3VudCkNCmltZ3NkX3RpZHkkdml2aWRfb3JhbmdlX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSR2aXZpZF9vcmFuZ2VfY291bnQpDQppbWdzZF90aWR5JHZpdmlkX3llbGxvd19jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkdml2aWRfeWVsbG93X2NvdW50KQ0KaW1nc2RfdGlkeSR2aXZpZF9ncmVlbl9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkdml2aWRfZ3JlZW5fY291bnQpDQppbWdzZF90aWR5JHZpdmlkX2N5YW5fY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHZpdmlkX2N5YW5fY291bnQpDQppbWdzZF90aWR5JHZpdmlkX2JsdWVfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHZpdmlkX2JsdWVfY291bnQpDQppbWdzZF90aWR5JHZpdmlkX3B1cnBsZV9jb3VudCA8LSBhcy5pbnRlZ2VyKGltZ3NkX3RpZHkkdml2aWRfcHVycGxlX2NvdW50KQ0KaW1nc2RfdGlkeSR2aXZpZF9tYWdfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHZpdmlkX21hZ19jb3VudCkNCmltZ3NkX3RpZHkkdml2aWRfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHZpdmlkX2NvdW50KQ0KDQppbWdzZF90aWR5JGdlbl9icmlnaHRfY291bnQgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JGdlbl9icmlnaHRfY291bnQpDQppbWdzZF90aWR5JGdlbl9kYXJrX2NvdW50IDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSRnZW5fZGFya19jb3VudCkNCg0KaW1nc2RfdGlkeSA8LSBpbWdzZF90aWR5ICU+JSBtdXRhdGUodG90YWxfcGl4ZWxzID0gZnVsbF9yZWRfY291bnQgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsX29yYW5nZV9jb3VudCArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGxfeWVsbG93X2NvdW50ICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbF9ncmVlbl9jb3VudCArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGxfY3lhbl9jb3VudCArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGxfYmx1ZV9jb3VudCArIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsX3B1cnBsZV9jb3VudCArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGxfbWFnX2NvdW50KQ0KDQojIG1vcmUgdHlwZSBjb252ZXJzaW9ucw0KaW1nc2RfdGlkeSR0b3RhbF9waXhlbHMgPC0gYXMuaW50ZWdlcihpbWdzZF90aWR5JHRvdGFsX3BpeGVscykNCmltZ3NkX3RpZHkkbGlnaHRfbWVhbl92YWwgPC0gYXMubnVtZXJpYyhpbWdzZF90aWR5JGxpZ2h0X21lYW5fdmFsKQ0KaW1nc2RfdGlkeSRwb3N0X251bV9yZWdpb25zIDwtIGFzLmludGVnZXIoaW1nc2RfdGlkeSRwb3N0X251bV9yZWdpb25zKQ0KaW1nc2RfdGlkeSRyX21lYW4gPC0gYXMubnVtZXJpYyhpbWdzZF90aWR5JHJfbWVhbikNCmltZ3NkX3RpZHkkZ19tZWFuIDwtIGFzLm51bWVyaWMoaW1nc2RfdGlkeSRnX21lYW4pDQppbWdzZF90aWR5JGJfbWVhbiA8LSBhcy5udW1lcmljKGltZ3NkX3RpZHkkYl9tZWFuKQ0KDQoNCiNlc3RhYmxpc2hpbmcgcmF0aW8gY29sdW1ucw0KaW1nc2RfcmF0aW8gPC0gaW1nc2RfdGlkeSAlPiUgDQogIG11dGF0ZShyYXRpb19wb3N0X3RvcF9oc2wgPSBwb3N0X3RvcF9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lDQogIG11dGF0ZShyYXRpb19wb3N0XzJfaHNsID0gcG9zdF8yX2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUNCiAgbXV0YXRlKHJhdGlvX3Bvc3RfM19oc2wgPSBwb3N0XzNfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JQ0KICBtdXRhdGUocmF0aW9fcG9zdF80X2hzbCA9IHBvc3RfNF9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lDQogIG11dGF0ZShyYXRpb19wb3N0XzVfaHNsID0gcG9zdF81X2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUNCiAgbXV0YXRlKHJhdGlvX3Bvc3RfNl9oc2wgPSBwb3N0XzZfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JQ0KICBtdXRhdGUocmF0aW9fZnVsbF9yZWQgPSBmdWxsX3JlZF9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lDQogIG11dGF0ZShyYXRpb19mdWxsX29yYWduZSA9IGZ1bGxfb3JhbmdlX2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUNCiAgbXV0YXRlKHJhdGlvX2Z1bGxfeWVsbG93ID0gZnVsbF95ZWxsb3dfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JQ0KICBtdXRhdGUocmF0aW9fZnVsbF9ncmVlbiA9IGZ1bGxfZ3JlZW5fY291bnQgLyB0b3RhbF9waXhlbHMpICU+JSAgDQogIG11dGF0ZShyYXRpb19mdWxsX2N5YW4gPSBmdWxsX2N5YW5fY291bnQgLyB0b3RhbF9waXhlbHMpICU+JSAgDQogIG11dGF0ZShyYXRpb19mdWxsX2JsdWUgPSBmdWxsX2JsdWVfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JSAgDQogIG11dGF0ZShyYXRpb19mdWxsX3B1cnBsZSA9IGZ1bGxfcHVycGxlX2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUgIA0KICBtdXRhdGUocmF0aW9fZnVsbF9tYWcgPSBmdWxsX21hZ19jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lICANCiAgbXV0YXRlKHJhdGlvX2Z1bGxfcmVkID0gZnVsbF9yZWRfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JQ0KICBtdXRhdGUocmF0aW9fdmlzaWJfcmVkID0gdmlzaWJfcmVkX2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUNCiAgbXV0YXRlKHJhdGlvX3Zpc2liX29yYWduZSA9IHZpc2liX29yYW5nZV9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lDQogIG11dGF0ZShyYXRpb192aXNpYl95ZWxsb3cgPSB2aXNpYl95ZWxsb3dfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JQ0KICBtdXRhdGUocmF0aW9fdmlzaWJfZ3JlZW4gPSB2aXNpYl9ncmVlbl9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lICANCiAgbXV0YXRlKHJhdGlvX3Zpc2liX2N5YW4gPSB2aXNpYl9jeWFuX2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUgIA0KICBtdXRhdGUocmF0aW9fdmlzaWJfYmx1ZSA9IHZpc2liX2JsdWVfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JSAgDQogIG11dGF0ZShyYXRpb192aXNpYl9wdXJwbGUgPSB2aXNpYl9wdXJwbGVfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JSAgDQogIG11dGF0ZShyYXRpb192aXNpYl9tYWcgPSB2aXNpYl9tYWdfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JQ0KICBtdXRhdGUocmF0aW9fdml2aWRfcmVkID0gdml2aWRfcmVkX2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUNCiAgbXV0YXRlKHJhdGlvX3ZpdmlkX29yYWduZSA9IHZpdmlkX29yYW5nZV9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lDQogIG11dGF0ZShyYXRpb192aXZpZF95ZWxsb3cgPSB2aXZpZF95ZWxsb3dfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JQ0KICBtdXRhdGUocmF0aW9fdml2aWRfZ3JlZW4gPSB2aXZpZF9ncmVlbl9jb3VudCAvIHRvdGFsX3BpeGVscykgJT4lICANCiAgbXV0YXRlKHJhdGlvX3ZpdmlkX2N5YW4gPSB2aXZpZF9jeWFuX2NvdW50IC8gdG90YWxfcGl4ZWxzKSAlPiUgIA0KICBtdXRhdGUocmF0aW9fdml2aWRfYmx1ZSA9IHZpdmlkX2JsdWVfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JSAgDQogIG11dGF0ZShyYXRpb192aXZpZF9wdXJwbGUgPSB2aXZpZF9wdXJwbGVfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JSAgDQogIG11dGF0ZShyYXRpb192aXZpZF9tYWcgPSB2aXZpZF9tYWdfY291bnQgLyB0b3RhbF9waXhlbHMpICU+JQ0KICBtdXRhdGUocmF0aW9fdml2aWQgPSB2aXZpZF9jb3VudCAvIHRvdGFsX3BpeGVscykNCg0KDQoNCg0KYGBgDQoNCiMjIEV4cGxvcmF0aW9uDQoNCiMjIyAxRCBIaXN0b2dyYW1zDQoNCi0gICBWaXZpZCAlDQoNCkR1ZSB0byBhIHZlcnkgbGFyZ2UgbnVtYmVyIG9mIGltYWdlcyB0aGF0IGhhdmUgZmV3ZXIgdGhhbiAyJSB2aXZpZCBwaXhlbHMsIFZpdmlkIFJhdGlvcyBhcmUgc2VwYXJhdGVkIG91dCBpbnRvIHRocmVlIHNlZ21lbnRzOiAwJSB2aXZpZCBwaXhlbHMsIDAtMSUgdml2aWQgcGl4ZWxzLCBhbmQgbW9yZSB0aGFuIDElIHZpdmlkIHBpeGVscw0KDQpgYGB7cn0NCg0Kdml2aWRfcmF0aW8gPC0gaW1nc2RfcmF0aW8gJT4lIHNlbGVjdChyYXRpb192aXZpZCkNCnZpdmlkX3JhdGlvJHJhdGlvX3ZpdmlkIDwtIGFzLm51bWVyaWModml2aWRfcmF0aW8kcmF0aW9fdml2aWQpDQoNCnZpdmlkX3JhdGlvIDwtIHZpdmlkX3JhdGlvICU+JSBtdXRhdGUoDQogIHJhdGlvX3JhbmsgPSBjYXNlX3doZW4oDQogICAgcmF0aW9fdml2aWQgPT0gMCB+IDAsIA0KICAgIHJhdGlvX3ZpdmlkID4gMCAmIHJhdGlvX3ZpdmlkIDwgMC4wMSB+IDEsIA0KICAgIHJhdGlvX3ZpdmlkID49IDAuMDEgfiAyKQ0KICApDQoNCnJyX2NvdW50cyA8LSBjb3VudCh2aXZpZF9yYXRpbywgcmF0aW9fcmFuaykNCg0KdnJyX3BpZSA8LSBwbG90X2x5KHJyX2NvdW50cywgbGFiZWxzPX5mYWN0b3IocmF0aW9fcmFuayksIHZhbHVlcz0gfm4sIHR5cGU9J3BpZScpDQoNCnZycl9waWUNCiMgMCAtPiBObyBWaXZpZCBQaXhlbHMNCiMgMSAtPiAwLTElIHZpdmlkIHBpeGVscw0KIyAyIC0+IG1vcmUgdGhhbiAxJSB2aXZpZCBwaXhlbHMNCmBgYA0KDQpMZXNzIHZpdmlkIHBpeGVscyB0aGFuIGV4cGVjdGVkISBXYXkgbGVzcyEgT25seSAzNTkgaW1hZ2VzL3N1YmltYWdlcyBoYWQgbW9yZSB0aGFuIDElIG9mIHBpeGVscyB3aXRoIG1vcmUgdGhhbiA3MCUgc2F0dXJhdGlvbiBhbmQgbGlnaHRuZXNzIGJldHdlZW4gNDAgYW5kIDcwJQ0KDQpOb3cgbGV0J3MgZ2V0IGJhY2sgdG8gc2VlaW5nIHRoZSBkaXN0cmlidXRpb24gb2YgdGhvc2UgMzU5IHZhbHVlczoNCg0KYGBge3J9DQoNCg0Kdml2aWRfaGlzdCA8LSB2aXZpZF9yYXRpbyAlPiUgZmlsdGVyKHJhdGlvX3JhbmsgPT0gMikNCg0KZmlnIDwtIHBsb3RfbHkodml2aWRfaGlzdCwNCiAgICAgICAgICAgICAgIHg9IH5yYXRpb192aXZpZCwNCiAgICAgICAgICAgICAgIHR5cGUgPSAiaGlzdG9ncmFtIikNCg0KZmlnDQpgYGANCg0KRnV0dXJlIGludmVzdGlnYXRpb24gd2lsbCBwZXJmb3JtIHRoZXNlIGNhbGN1bGF0aW9ucyBvbiB0aGUgbWFpbiBkYXRhZnJhbWUgaW4gb3JkZXIgdG8gYWNjZXNzIHNwZWNpZmljIGltYWdlIElEcyBhbmQgZXZhbHVhdGUgdGhlIHN1YmplY3RpdmUgcXVhbGl0aWVzIG9mIHRoZSB0b3Agdml2aWQgaW1hZ2VzLg0KDQotICAgUG9zdC1udW0gKGZpbHRlciBieSBpbWcgc2VnbWVudCkNCg0KSW52ZXN0aWdhdGluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIGhvdyBtYW55IHBvc3Rlcml6YXRpb24gImNsdW1wcyIgd2VyZSBpbiBlYWNoIGltYWdlDQoNCmBgYHtyfQ0KDQojZmlyc3Qgc2V0dGluZyB1cCBmaWx0ZXJzIGZvciBtb3JlIHVzZWZ1bCBmYWNldGluZyBnb2luZyBmb3J3YXJkDQpyX2ZpZ19maWx0ZXJfMCA8LSBpbWdzZF9yYXRpbyAlPiUgZmlsdGVyKGltZ3NkX3JhdGlvJHN1Yl9pbWcgPT0gMCkNCnJfZmlnX2ZpbHRlcl9hIDwtIGltZ3NkX3JhdGlvW2ltZ3NkX3JhdGlvJHN1Yl9pbWcgJWluJSBjKDEsIDIsIDMpLCBdDQpyX2ZpZ19maWx0ZXJfYiA8LSBpbWdzZF9yYXRpb1tpbWdzZF9yYXRpbyRzdWJfaW1nICVpbiUgYyg0LCA1LCA2KSwgXQ0Kcl9maWdfZmlsdGVyX2MgPC0gaW1nc2RfcmF0aW9baW1nc2RfcmF0aW8kc3ViX2ltZyAlaW4lIGMoNywgOCwgOSksIF0NCg0KDQpmaWdfMCA8LSBnZ3Bsb3Qocl9maWdfZmlsdGVyXzAsDQogICAgICAgICAgICAgICBhZXMoeD1wb3N0X251bV9yZWdpb25zKSkgKyANCiAgICAgICAgICAgICAgICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUpDQoNCmZpZ19hIDwtIGdncGxvdChyX2ZpZ19maWx0ZXJfYSwNCiAgICAgICAgICAgICAgYWVzKHg9cG9zdF9udW1fcmVnaW9ucykpICsgDQogICAgICAgICAgICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSkgKw0KICAgICAgICAgICAgICBmYWNldF9ncmlkICh+IHN1Yl9pbWcpDQoNCmZpZ19iIDwtIGdncGxvdChyX2ZpZ19maWx0ZXJfYiwNCiAgICAgICAgICAgICAgYWVzKHg9cG9zdF9udW1fcmVnaW9ucykpICsgDQogICAgICAgICAgICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSkgKw0KICAgICAgICAgICAgICBmYWNldF9ncmlkICh+IHN1Yl9pbWcpDQoNCmZpZ19jIDwtIGdncGxvdChyX2ZpZ19maWx0ZXJfYywNCiAgICAgICAgICAgICAgYWVzKHg9cG9zdF9udW1fcmVnaW9ucykpICsgDQogICAgICAgICAgICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSkgKw0KICAgICAgICAgICAgICBmYWNldF9ncmlkICh+IHN1Yl9pbWcpDQoNCmZpZ18wYiA8LSBnZ3Bsb3Qocl9maWdfZmlsdGVyXzAsDQogICAgICAgICAgICAgICBhZXMoeD1wb3N0X251bV9yZWdpb25zKSkgKyANCiAgICAgICAgICAgICAgICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUwKQ0KDQpmaWdfMA0KZmlnXzBiDQpmaWdfYQ0KZmlnX2INCmZpZ19jDQoNCmBgYA0KDQpObyBodWdlIGNvbmNsdXNpb25zIHRvIGRyYXcgYWJvdXQgdGhlIGRpc3RyaWJ1dGlvbiBpbiBzdWJfaW1hZ2UgMCwgb3RoZXIgdGhhbiB0aGUgeC1zY2FsZSBpcyBzaG93biBhcyByb3VnaGx5IDEweCAoc2xpZ2h0bHkgbGVzcykgdGhhbiB0aGUgc3ViaW1hZ2VzLiBBbHNvIG1pbGRseSBpbnRlcmVzdGluZyAoYWxzbyBvbiBzdWJfaW1nIDApLCB0aGUgbWF4IGNvdW50IGZvciBhbnkgZ2l2ZW4gYmluIChhdCBiaW4td2lkdGggNSksIGlzIGEgZnJhY3Rpb24gb2YgdGhlIHNjYWxlIG9mIHRoZSBvdGhlciBzdWJfaW1hZ2VzLiBCdW1waW5nIHRoZSBzdWJfaW1nIDAgYmlud2lkdGggdXAgdG8gNTAgKDEweCksIGJyaW5ncyBpdCBtb3JlIGluIGxpbmUgd2l0aCB0aGUgY291bnRzIGluIHRoZSBzdWJfaW1hZ2VzLCBidXQgdGhlIGxvbmcgdGFpbCBvbiBzdWJfaW1nIDAga2VlcHMgaXQgZnJvbSBtYXRjaGluZyBleGFjdGx5Lg0KDQpPdGhlcndpc2UsIHRoZSBvbmx5IG5vdGV3b3J0aHkgb2JzZXJ2YXRpb24gaXMgdGhlIGxlc3MtaW50ZW5zZSByaWdodCBza2V3IG9uIHN1Yl9pbWFnZXMgMiwgNSwgYW5kIDguDQoNClRoZXNlIHN1Yi1pbWFnZXMgcmVwcmVzZW50IHRoZSBob3Jpem9udGFsIG1pZGRsZSB0aGlyZCBvZiB0aGVpciBzb3VyY2UgaW1hZ2VzLCBtaW1pY2tpbmcgdGhlIGFydGlzdGljIHByaW5jaXBsZXMgb2YgdGhlIHJ1bGUgb2YgdGhpcmRzIGFuZCBwdXR0aW5nIGZvY2FsIHBvaW50cyBuZWFyIHRoZSBjZW50ZXIgb2YgdGhlIGltYWdlLg0KDQotICAgRXhpZiBicmlnaHRuZXNzDQoNCkVYSUYgZGF0YSwgSSBoYXZlIG5vdCBmb3JzYWtlbiB5b3UuDQoNCmBgYHtyfQ0KDQoNCg0KZmlnIDwtIGdncGxvdChleGlmX3RpZHksDQogICAgICAgICAgICAgICBhZXMoeD1icmlnaHRuZXNzX3ZhbHVlKSkgKyANCiAgICAgICAgICAgICAgICBnZW9tX2hpc3RvZ3JhbShiaW5zPTI1MCkNCg0KZmlnDQpgYGANCg0KVGhhdCdzIHRoZSBtb3N0IG5vcm1hbCBkaXN0cmlidXRpb24gd2UndmUgc2VlbiB5ZXQhIEJ1dCBzdGlsbCBwcmV0dHkgaXJyZWd1bGFyLiBMZXQncyBzZWUgaG93IGl0IGNvbXBhcmVzIHRvIHRoZSBIU0wgZGF0YQ0KDQotICAgTWVhbl9saWdodG5lc3MNCg0KYGBge3J9DQoNCmZpZ19hbGwgPC0gZ2dwbG90KGltZ3NkX3JhdGlvLA0KICAgICAgICAgICAgICAgYWVzKHg9bGlnaHRfbWVhbl92YWwpKSArIA0KICAgICAgICAgICAgICAgIGdlb21faGlzdG9ncmFtKGJpbnM9MjUwKQ0KDQpmaWdfMCA8LSBnZ3Bsb3Qocl9maWdfZmlsdGVyXzAsDQogICAgICAgICAgICAgICBhZXMoeD1saWdodF9tZWFuX3ZhbCkpICsgDQogICAgICAgICAgICAgICAgZ2VvbV9oaXN0b2dyYW0oYmlucz0yNTApDQoNCmZpZ19hIDwtIGdncGxvdChyX2ZpZ19maWx0ZXJfYSwNCiAgICAgICAgICAgICAgYWVzKHg9bGlnaHRfbWVhbl92YWwpKSArIA0KICAgICAgICAgICAgICBnZW9tX2hpc3RvZ3JhbShiaW5zPTI1MCkgKw0KICAgICAgICAgICAgICBmYWNldF9ncmlkKH4gc3ViX2ltZykNCg0KZmlnX2IgPC0gZ2dwbG90KHJfZmlnX2ZpbHRlcl9iLA0KICAgICAgICAgICAgICBhZXMoeD1saWdodF9tZWFuX3ZhbCkpICsgDQogICAgICAgICAgICAgIGdlb21faGlzdG9ncmFtKGJpbnM9MjUwKSArDQogICAgICAgICAgICAgIGZhY2V0X2dyaWQofiBzdWJfaW1nKQ0KDQpmaWdfYyA8LSBnZ3Bsb3Qocl9maWdfZmlsdGVyX2MsDQogICAgICAgICAgICAgIGFlcyh4PWxpZ2h0X21lYW5fdmFsKSkgKyANCiAgICAgICAgICAgICAgZ2VvbV9oaXN0b2dyYW0oYmlucz0yNTApICsNCiAgICAgICAgICAgICAgZmFjZXRfZ3JpZCh+IHN1Yl9pbWcpDQoNCg0KZmlnX2FsbA0KZmlnXzANCmZpZ19hDQpmaWdfYg0KZmlnX2MNCmBgYA0KDQpPYnNlcnZhdGlvbnM6IFZhcmlhdGlvbiBhbW9uZyBzdWItaW1hZ2VzIGlzIGZhaXJseSBtaW5vciwgd2l0aCBzb21lIHNvbWUgcG9zc2liaWxpdHkgdGhhdCBjb3JuZXIgcXVhZHJhbnRzICgxLCAzLCA3LCA5KSBoYXZlIHdpZGVyIGRpc3RyaWJ1dGlvbnMgb3ZlcmFsbC4gTGlnaHRuZXNzIHZhbHVlcyBwcm9jZXNzZWQgZm9yIHRoaXMgZGF0YXNldCBhcmUgY2VudGVyZWQgYXJvdW5kIDAuNjI1IHdpdGggYSBzbGlnaHQgbGVmdCBza2V3LiBXaGlsZSB0aGUgRVhJRiBkYXRhIGhhcyBhIGxlc3Mtbm9ybWFsIGRpc3RyaWJ1dGlvbiwgaXQgZG9lcyBzZWVtIHRvIGhhdmUgYSBjZW50ZXIgc2xpZ2h0bHkgdG8gdGhlIHJpZ2h0IG9mIGl0cyBiYXNlbGluZSB2YWx1ZSwgYnV0IHRoZSBpcnJlZ3VsYXJpdHkgb2YgdGhlIHNoYXBlIGxvb2tzIG1vcmUgbGlrZSBhIHJpZ2h0IHNrZXcgdGhhbiBsZWZ0Lg0KDQotICAgKHIvZy9iKSBtZWFuXA0KDQpgYGB7cn0NCg0KZmlnIDwtIHBsb3RfbHkoaW1nc2RfcmF0aW8sIGFscGhhID0gLjQpDQpmaWcgPC0gZmlnICU+JSBhZGRfaGlzdG9ncmFtKHggPSB+cl9tZWFuKQ0KZmlnIDwtIGZpZyAlPiUgYWRkX2hpc3RvZ3JhbSh4ID0gfmdfbWVhbikNCmZpZyA8LSBmaWcgJT4lIGFkZF9oaXN0b2dyYW0oeCA9IH5iX21lYW4pDQpmaWcgPC0gZmlnICU+JSBsYXlvdXQoYmFybW9kZSA9ICdvdmVybGF5JywgY29sb3J3YXk9YygncmVkJywgJ2dyZWVuJywgJ2JsdWUnKSkNCg0KZmlnDQpgYGANCg0KT2JzZXJ2YXRpb246IE5vIHNwZWNpYWwgaW5mb3JtYXRpb24gaGVyZS4gQXMgZXhwZWN0ZWQsIGl0IGxvb2tzIGEgbG90IGxpa2UgdGhlIG1lYW4gbGlnaHRuZXNzIHZhbHVlIGRpc3RyYnV0aW9uLiBBbG1vc3QgYXMgaWYgcitnK2IgPSBsaWdodC4uLi4NCg0KLSAgIERhdGUgZGlzdHJpYnV0aW9uICh3aXRoIGFuZCB3aXRob3V0IHllYXIpDQoNCmBgYHtyfQ0KDQpmaWcgPC0gcGxvdF9seShleGlmX3RpZHksIHggPSB+ZGF0ZSwgdHlwZSA9ICdoaXN0b2dyYW0nKQ0KDQpmaWdfYWxsIDwtIHBsb3RfbHkoZXhpZl90aWR5LCB4ID0gfmZ1bGxfZGF0ZSwgdHlwZSA9ICdoaXN0b2dyYW0nKQ0KDQpmaWcNCmZpZ19hbGwNCmBgYA0KDQpPYnNlcnZhdGlvbjogRXZlbiB3aGVuIGFkanVzdGluZyBmb3IgeWVhciwgdGhlIGRpc3RyaWJ1dGlvbiBvZiBpbWFnZXMgaXMgaGlnaGx5IGlycmVndWxhci4gQXMgYSBtb250aCwgQXVndXN0IGlzIG92ZXItcmVwcmVzZW50ZWQsIGFuZCBhcyBhbiBpbmRpdmlkdWFsIGRhdGUsIFdpbGwgYW5kIFRheWxvcidzIHdlZGRpbmcgaXMgb3Zlci1yZXByZXNlbnRlZC4NCg0KYGBge3J9DQpleGlmX3RpZHkkdGltZSA8LSBzdHJwdGltZShleGlmX3RpZHkkdGltZSwgZm9ybWF0ID0gIiVIOiVNJVMiKQ0KDQpmaWcgPC0gcGxvdF9seShleGlmX3RpZHksIHggPSB+dGltZSwgdHlwZSA9ICdoaXN0b2dyYW0nKQ0KZmlnDQpgYGANCg0KIyMgQW5hbHlzaXMNCg0KRmluYWwgZGF0YSBwcmVwIQ0KDQpTbGltbWluZyBkb3duIHRoZSBFWElGIGRhdGEgZnJhbWUuLi4uDQoNCmBgYHtyfQ0KDQojc2xpbW1pbmcgZG93biBleGlmIGNvbHVtbnMuLi4uDQoNCnNpbXBsZV9leGlmIDwtIGV4aWZfdGlkeSAlPiUgc2VsZWN0KGMoZmxpY2tyX2lkLCBkYXRlLCBtb250aCwgdGltZSwgc29mdHdhcmUsIGpmaWZ2ZXJzaW9uLCBicmlnaHRuZXNzX3ZhbHVlKSkNCmBgYA0KDQouLi4gYW5kIGFkZGluZyB0aG9zZSBFWElGIGNvbHVtbnMgdG8gdGhlIG1haW4gZGF0YWZyYW1lDQoNCmBgYHtyfQ0KDQpzaW1wbGVfZXhpZiA8LSBzaW1wbGVfZXhpZiAlPiUNCiAgbXV0YXRlKGZsaWNrcl9pZCA9IGFzLmNoYXJhY3RlcihmbGlja3JfaWQpKQ0KDQojIGJyaW5naW5nIGl0IGFsbCBmdWxsIGNpcmNsZSB3aXRoIHRoaXMgdmFyaWFibGUgY2hvaWNlDQoNCmFsbF9pbWdfZGF0YSA8LSBpbWdzZF9yYXRpbyAlPiUgDQogIGxlZnRfam9pbihzaW1wbGVfZXhpZiwgYnkgPSBjKCJ1c2luZ19pZCIgPSAiZmxpY2tyX2lkIikpDQoNCiN0aGlzIHRocm93cyBhIHdhcm5pbmcgYWJvdXQgbWFueS10by1tYW55IHJlbGF0aW9uc2hpcHMsIGJ1dCB0aGlzIGlzIGV4cGVjdGVkIGJlaGF2aW9yDQpgYGANCg0KVGhpcyBzcGFjZSByZXNlcnZlZCBmb3IgcmUtY3JlYXRpbmcgc3ViLWZyYW1lcyBmb3IgJ2ZhY2V0aW5nJw0KDQpgYGB7cn0NCg0KDQpgYGANCg0KQXQgbGFzdCwgbGV0J3MgcmV2aXNpdCB0aG9zZSBoeXBvdGhlc2VzDQoNCiMjIyBIMSAtIEh1ZSB2cyBEYXRlDQoNCiRIbzokIFRoZXJlIGlzIG5vIGNvcnJlbGF0aW9uIGJldHdlZW4gdGltZSBvZiB5ZWFyIGFuZCBjb2xvciB2YWx1ZXMNCg0KJEhhOiQgV2FybSBjb2xvciB2YWx1ZXMgYXJlIG1vcmUgcHJvbWluZW50IGJldHdlZW4gTWF5IGFuZCBTZXB0ZW1iZXINCg0KR3V0IENoZWNrIC0gRHVlIHRvIHRoZSB1bmV2ZW4gZGlzdHJpYnV0aW9uIG9mIHNhbXBsZSBpbWFnZXMgb3ZlciB0aW1lLCBJIGRvbid0IGhhdmUgYSBzdHJvbmcgZXhwZWN0YXRpb24gb2YgdmFsaWQgcmVzdWx0cy4NCg0KYGBge3J9DQoNCmV4aWZfdGlkeSRtb250aCA8LSBhcy5pbnRlZ2VyKGV4aWZfdGlkeSRtb250aCkNCg0Kc2Vhc29uX3NwbGl0IDwtIGV4aWZfdGlkeSAlPiUgbXV0YXRlKA0KICBzZWFzb24gPSBjYXNlX3doZW4oDQogICAgbW9udGggPj0gNSAmIG1vbnRoIDw9IDkgfiAxLCBUUlVFIH4gMCkNCiAgKQ0KDQpzZWFzb25fY291bnRzIDwtIHRhYmxlKHNlYXNvbl9zcGxpdCRzZWFzb24pDQpzZWFzb25fZGF0YSA8LSBkYXRhLmZyYW1lKHJhdGlvID0gbmFtZXMoc2Vhc29uX2NvdW50cyksIGNvdW50ID0gYXMudmVjdG9yKHNlYXNvbl9jb3VudHMpKQ0KDQpzZWFzb25fZGF0YQ0KYGBgDQoNCk9uIHRoZSBvdGhlciBoYW5kLCBzdW1tZXIgdmFsdWVzICg2MjIpIGRvbid0IG91dHJhZ2VvdXNseSBvdXRudW1iZXIgbm9uLXN1bW1lciB2YWx1ZXMuDQoNCk5vdyB0byBjb2xsZWN0IHRoZSBodWUgaW5mb3JtYXRpb24gd2Ugd2lsbCBiZSBjb21wYXJpbmcNCg0KYGBge3J9DQoNCmFsbF9pbWdfZGF0YSA8LSBhbGxfaW1nX2RhdGEgJT4lIA0KICBtdXRhdGUodmlzaWJfd2FybSA9IHZpc2liX3JlZF9jb3VudCArIHZpc2liX29yYW5nZV9jb3VudCArIHZpc2liX3llbGxvd19jb3VudCArIHZpc2liX21hZ19jb3VudCkgJT4lDQogIG11dGF0ZSh2aXNpYl9jb29sID0gdmlzaWJfZ3JlZW5fY291bnQgKyB2aXNpYl9jeWFuX2NvdW50ICsgdmlzaWJfYmx1ZV9jb3VudCArIHZpc2liX3B1cnBsZV9jb3VudCkNCiAgDQpoMV9mcmFtZSA8LSBhbGxfaW1nX2RhdGEgJT4lIA0KICBzZWxlY3QoYyh1c2luZ19pZCwgc3ViX2ltZywgdG90YWxfcGl4ZWxzLCB2aXNpYl93YXJtLCB2aXNpYl9jb29sLCBkYXRlLCBtb250aCkpICU+JSANCiAgbXV0YXRlKHdhcm1fcmF0aW8gPSB2aXNpYl93YXJtIC8gdG90YWxfcGl4ZWxzKSAlPiUgDQogIG11dGF0ZShjb29sX3JhdGlvID0gdmlzaWJfY29vbC90b3RhbF9waXhlbHMpICU+JSANCiAgbXV0YXRlKHNlYXNvbiA9IGNhc2Vfd2hlbihtb250aCA+PSA1ICYgbW9udGggPD0gOSB+IDEsIFRSVUUgfiAwKSkgJT4lDQogIG11dGF0ZShyYXRpb19kaWZmID0gd2FybV9yYXRpbyAtIGNvb2xfcmF0aW8pDQpgYGANCg0KIyMjIyBUZXN0aW5nIFRpbWUNCg0KYGBge3J9DQp0LnRlc3Qod2FybV9yYXRpbyB+IHNlYXNvbiwgaDFfZnJhbWUpDQpgYGANCg0KIyBDb25jbHVzaW9uDQoNCkRlc3BpdGUgbXkgaGVzaXRhdGlvbiwgdGhlIHJlc3VsdHMgb2YgdGhlIFQtdGVzdCBhcmUgc3Ryb25nbHkgaW4gZmF2b3Igb2YgcmVqZWN0aW5nIHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGVyZSBpcyBubyBkaWZmZXJlbmNlIGluIHZpc2libGUgd2FybXRoIHRocm91Z2hvdXQgdGhlIHllYXIuIEEgaGlnaCB0LXZhbHVlIGFuZCBsb3cgcC12YWx1ZSBhcmUgYm90aCBpbiBzdXBwb3J0IG9mIHRoaXMgY29uY2x1c2lvbi4NCg0KIyBQb3N0IFNjcmlwdDoNCg0KQWRkaXRpb25hbCBIeXBvdGhlc2VzOg0KDQojIyMgSDIgLSBMaWdodG5lc3MgdnMgVGltZQ0KDQokSG86JCBUaGVyZSBpcyBubyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRpbWUgb2YgZGF5IGFuZCBsaWdodG5lc3MgdmFsdWVzDQoNCiRIYTokIExpZ2h0bmVzcyB2YWx1ZXMgYXJlIGhpZ2hlciBiZXR3ZWVuIDYgYW0gYW5kIDZwbQ0KDQojIyMgSDMgLSBTYXR1cmF0aW9uIHZzIFN1YmplY3QNCg0KJEhvOiQgVGhlcmUgaXMgbm8gY29ycmVsYXRpb24gYmV0d2VlbiBzYXR1cmF0aW9uIGFuZCBiZWluZyBhIHBpY3R1cmUgb2YgbXkgY2F0DQoNCiRIYTokIExvdyBzYXR1cmF0aW9uIHZhbHVlcyBhcmUgaW5jcmVhc2luZ2x5IGNvbW1vbiBvdmVyIHRpbWUsIGVzcGVjaWFsbHkgaW4gY2VudHJhbCBzdWItaW1hZ2VzDQoNCiMjIyBINCAtIFZpdmlkbmVzcyB2cyBJbWFnZSBUeXBlDQoNCiRIbzokIFZpdmlkIHJhdGlvIChwZXJjZW50YWdlIG9mIHZpdmlkIHBpeGVscykgaXMgdW5pZm9ybWx5IGRpc3RyaWJ1dGVkIGFtb25nIGFsbCBTb2Z0d2FyZSB0eXBlcw0KDQokSGE6JCBWaXZpZCByYXRpbyBpcyBjb25zaXN0ZW50bHkgaGlnaGVzdCBpbiBTbG93IFNodXR0ZXIgQ2FtIHBob3RvcyB3aXRob3V0IEpGSUYgdmFsdWVzDQo=